using System;
using System.Collections.Generic;
using Engine.Serialization;

namespace RuthlessConquest {
    public struct Version : IEquatable<Version>, IComparable<Version> {
        [HumanReadableConverter(typeof(Version))]
        public class VersionHumanReadableConverter : IHumanReadableConverter {
            public string ConvertToString(object value) {
                Version version = (Version)value;
                return HumanReadableConverter.ValuesListToString('.', version.Major, version.Minor, version.Build, version.Revision);
            }

            public object ConvertFromString(Type type, string data) {
                byte[] array = HumanReadableConverter.ValuesListFromString<byte>('.', data);
                if (array.Length == 2) {
                    Version version = default;
                    version.Major = array[0];
                    version.Minor = array[1];
                    return version;
                }
                if (array.Length == 3) {
                    Version version = default;
                    version.Major = array[0];
                    version.Minor = array[1];
                    version.Build = array[2];
                    return version;
                }
                if (array.Length == 4) {
                    Version version = default;
                    version.Major = array[0];
                    version.Minor = array[1];
                    version.Build = array[2];
                    version.Revision = array[3];
                    return version;
                }
                throw new Exception();
            }
        }

        public int Value;

        public byte Major {
            get => (byte)(Value >> 24);
            set => Value = (Value & 0xFFFFFF) | (value << 24);
        }

        public byte Minor {
            get => (byte)(Value >> 16);
            set => Value = (Value & -16711681) | (value << 16);
        }

        public byte Build {
            get => (byte)(Value >> 8);
            set => Value = (Value & -65281) | (value << 8);
        }

        public byte Revision {
            get => (byte)Value;
            set => Value = (Value & -256) | value;
        }

        public Version(byte[] bytes, int startIndex) => Value = BitConverter.ToInt32(bytes, startIndex);

        public Version(int value) => Value = value;

        public Version(byte major, byte minor, byte build, byte revision) => Value = (major << 24) | (minor << 16) | (build << 8) | revision;

        public Version GetNetworkProtocolVersion() => new(Major, Minor, 0, 0);

        public bool Equals(Version other) => Value == other.Value;

        public byte[] ToByteArray() => BitConverter.GetBytes(Value);

        public override string ToString() => ToString();

        public string ToString(int components = 4) {
            return components switch {
                4 => $"{Major}.{Minor}.{Build}.{Revision}",
                3 => $"{Major}.{Minor}.{Build}",
                2 => $"{Major}.{Minor}",
                1 => $"{Major}",
                0 => "",
                _ => throw new InvalidOperationException()
            };
        }

        public override bool Equals(object obj) {
            if (!(obj is Version other)) {
                return false;
            }
            return Equals(other);
        }

        public override int GetHashCode() => Value;

        public int CompareTo(Version other) => Comparer<uint>.Default.Compare((uint)Value, (uint)other.Value);

        public static bool operator ==(Version v1, Version v2) => v1.Equals(v2);

        public static bool operator !=(Version v1, Version v2) => !v1.Equals(v2);

        public static bool operator <(Version v1, Version v2) => (uint)v1.Value < (uint)v2.Value;

        public static bool operator >(Version v1, Version v2) => (uint)v1.Value > (uint)v2.Value;
    }
}